1   /*
2    * Copyright (C) 2007 The Guava Authors
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    * http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  
17  package com.google.common.io;
18  
19  import static com.google.common.base.Preconditions.checkNotNull;
20  import static com.google.common.base.Preconditions.checkPositionIndexes;
21  
22  import com.google.common.annotations.Beta;
23  
24  import java.io.Closeable;
25  import java.io.EOFException;
26  import java.io.IOException;
27  import java.io.Reader;
28  import java.io.Writer;
29  import java.nio.CharBuffer;
30  import java.util.ArrayList;
31  import java.util.List;
32  
33  /**
34   * Provides utility methods for working with character streams.
35   *
36   * <p>All method parameters must be non-null unless documented otherwise.
37   *
38   * <p>Some of the methods in this class take arguments with a generic type of
39   * {@code Readable & Closeable}. A {@link java.io.Reader} implements both of
40   * those interfaces. Similarly for {@code Appendable & Closeable} and
41   * {@link java.io.Writer}.
42   *
43   * @author Chris Nokleberg
44   * @author Bin Zhu
45   * @author Colin Decker
46   * @since 1.0
47   */
48  @Beta
49  public final class CharStreams {
50    private static final int BUF_SIZE = 0x800; // 2K chars (4K bytes)
51  
52    private CharStreams() {}
53  
54    /**
55     * Copies all characters between the {@link Readable} and {@link Appendable}
56     * objects. Does not close or flush either object.
57     *
58     * @param from the object to read from
59     * @param to the object to write to
60     * @return the number of characters copied
61     * @throws IOException if an I/O error occurs
62     */
63    public static long copy(Readable from, Appendable to) throws IOException {
64      checkNotNull(from);
65      checkNotNull(to);
66      CharBuffer buf = CharBuffer.allocate(BUF_SIZE);
67      long total = 0;
68      while (from.read(buf) != -1) {
69        buf.flip();
70        to.append(buf);
71        total += buf.remaining();
72        buf.clear();
73      }
74      return total;
75    }
76  
77    /**
78     * Reads all characters from a {@link Readable} object into a {@link String}.
79     * Does not close the {@code Readable}.
80     *
81     * @param r the object to read from
82     * @return a string containing all the characters
83     * @throws IOException if an I/O error occurs
84     */
85    public static String toString(Readable r) throws IOException {
86      return toStringBuilder(r).toString();
87    }
88  
89    /**
90     * Reads all characters from a {@link Readable} object into a new
91     * {@link StringBuilder} instance. Does not close the {@code Readable}.
92     *
93     * @param r the object to read from
94     * @return a {@link StringBuilder} containing all the characters
95     * @throws IOException if an I/O error occurs
96     */
97    private static StringBuilder toStringBuilder(Readable r) throws IOException {
98      StringBuilder sb = new StringBuilder();
99      copy(r, sb);
100     return sb;
101   }
102 
103   /**
104    * Reads all of the lines from a {@link Readable} object. The lines do
105    * not include line-termination characters, but do include other
106    * leading and trailing whitespace.
107    *
108    * <p>Does not close the {@code Readable}. If reading files or resources you
109    * should use the {@link Files#readLines} and {@link Resources#readLines}
110    * methods.
111    *
112    * @param r the object to read from
113    * @return a mutable {@link List} containing all the lines
114    * @throws IOException if an I/O error occurs
115    */
116   public static List<String> readLines(Readable r) throws IOException {
117     List<String> result = new ArrayList<String>();
118     LineReader lineReader = new LineReader(r);
119     String line;
120     while ((line = lineReader.readLine()) != null) {
121       result.add(line);
122     }
123     return result;
124   }
125 
126   /**
127    * Streams lines from a {@link Readable} object, stopping when the processor
128    * returns {@code false} or all lines have been read and returning the result
129    * produced by the processor. Does not close {@code readable}. Note that this
130    * method may not fully consume the contents of {@code readable} if the
131    * processor stops processing early.
132    *
133    * @throws IOException if an I/O error occurs
134    * @since 14.0
135    */
136   public static <T> T readLines(
137       Readable readable, LineProcessor<T> processor) throws IOException {
138     checkNotNull(readable);
139     checkNotNull(processor);
140 
141     LineReader lineReader = new LineReader(readable);
142     String line;
143     while ((line = lineReader.readLine()) != null) {
144       if (!processor.processLine(line)) {
145         break;
146       }
147     }
148     return processor.getResult();
149   }
150 
151   /**
152    * Discards {@code n} characters of data from the reader. This method
153    * will block until the full amount has been skipped. Does not close the
154    * reader.
155    *
156    * @param reader the reader to read from
157    * @param n the number of characters to skip
158    * @throws EOFException if this stream reaches the end before skipping all
159    *     the characters
160    * @throws IOException if an I/O error occurs
161    */
162   public static void skipFully(Reader reader, long n) throws IOException {
163     checkNotNull(reader);
164     while (n > 0) {
165       long amt = reader.skip(n);
166       if (amt == 0) {
167         // force a blocking read
168         if (reader.read() == -1) {
169           throw new EOFException();
170         }
171         n--;
172       } else {
173         n -= amt;
174       }
175     }
176   }
177 
178   /**
179    * Returns a {@link Writer} that simply discards written chars.
180    *
181    * @since 15.0
182    */
183   public static Writer nullWriter() {
184     return NullWriter.INSTANCE;
185   }
186 
187   private static final class NullWriter extends Writer {
188 
189     private static final NullWriter INSTANCE = new NullWriter();
190 
191     @Override
192     public void write(int c) {
193     }
194 
195     @Override
196     public void write(char[] cbuf) {
197       checkNotNull(cbuf);
198     }
199 
200     @Override
201     public void write(char[] cbuf, int off, int len) {
202       checkPositionIndexes(off, off + len, cbuf.length);
203     }
204 
205     @Override
206     public void write(String str) {
207       checkNotNull(str);
208     }
209 
210     @Override
211     public void write(String str, int off, int len) {
212       checkPositionIndexes(off, off + len, str.length());
213     }
214 
215     @Override
216     public Writer append(CharSequence csq) {
217       checkNotNull(csq);
218       return this;
219     }
220 
221     @Override
222     public Writer append(CharSequence csq, int start, int end) {
223       checkPositionIndexes(start, end, csq.length());
224       return this;
225     }
226 
227     @Override
228     public Writer append(char c) {
229       return this;
230     }
231 
232     @Override
233     public void flush() {
234     }
235 
236     @Override
237     public void close() {
238     }
239 
240     @Override
241     public String toString() {
242       return "CharStreams.nullWriter()";
243     }
244   }
245 
246   /**
247    * Returns a Writer that sends all output to the given {@link Appendable}
248    * target. Closing the writer will close the target if it is {@link
249    * Closeable}, and flushing the writer will flush the target if it is {@link
250    * java.io.Flushable}.
251    *
252    * @param target the object to which output will be sent
253    * @return a new Writer object, unless target is a Writer, in which case the
254    *     target is returned
255    */
256   public static Writer asWriter(Appendable target) {
257     if (target instanceof Writer) {
258       return (Writer) target;
259     }
260     return new AppendableWriter(target);
261   }
262 
263   // TODO(user): Remove these once Input/OutputSupplier methods are removed
264 
265   static Reader asReader(final Readable readable) {
266     checkNotNull(readable);
267     if (readable instanceof Reader) {
268       return (Reader) readable;
269     }
270     return new Reader() {
271       @Override
272       public int read(char[] cbuf, int off, int len) throws IOException {
273         return read(CharBuffer.wrap(cbuf, off, len));
274       }
275 
276       @Override
277       public int read(CharBuffer target) throws IOException {
278         return readable.read(target);
279       }
280 
281       @Override
282       public void close() throws IOException {
283         if (readable instanceof Closeable) {
284           ((Closeable) readable).close();
285         }
286       }
287     };
288   }
289 }